/**
  ******************************************************************************
  * @file    Rtc.c
  * @author  Qilong Huang
  * @version V0.0.1
  * @date    04-03-2020
  * @brief   This file contains functions to initialize the OS.  
  *				 
  *          
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; KION(SZ) R&D Center
  *
  * Licensed under KION, (the "License");
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/

/*===============================================[ private includes  ]================================================*/

#include "rtc.h"


/*===============================================[ private defines   ]================================================*/
#define USE_STM32_LSE
/*===============================================[ public  defines   ]================================================*/
u32 cnt_temp;
t_tm sys_time = {0};
t_tm set_time;

/*================================================[ inline functions ]================================================*/

/*================================================[ public functions ]================================================*/


/**
* @brief	Configuration the RTC.
* @param	none
* @note     none
* @retval   none
*/
void RTC_Configuration(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP ,ENABLE);
    PWR_BackupAccessCmd(ENABLE);
//	BKP_DeInit();
#if defined (RTC_CLOCK_SOURCE_LSI)  /* LSI used as RTC source clock*/
    RCC_LSICmd(ENABLE);
    while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY)== RESET)
    {
        ;
    }
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);//RCC_RTCCLKSource_LSI--40KHz
    RCC_RTCCLKCmd(ENABLE);
    RTC_WaitForSynchro();
    RTC_WaitForLastTask();
    RTC_ITConfig(RTC_IT_SEC, ENABLE);
    RTC_WaitForLastTask();
    RTC_SetPrescaler(40000);//40kHz LSI
    RTC_WaitForLastTask();
#elif defined (RTC_CLOCK_SOURCE_LSE) /* LSE used as RTC source clock */
    
	if(BKP_ReadBackupRegister(BKP_DR19) != 0x5a5a)
	{
		BKP_DeInit();
		RCC_LSEConfig(RCC_LSE_ON);
		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
		{

		}
		/* Select LSE as RTC Clock Source */
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

		/* Enable RTC Clock */
		RCC_RTCCLKCmd(ENABLE);

		/* Wait for RTC registers synchronization */
		RTC_WaitForSynchro();

		/* Wait until last write operation on RTC registers has finished */
		RTC_WaitForLastTask();

		/* Enable the RTC Second */
		RTC_ITConfig(RTC_IT_SEC, ENABLE);

		/* Wait until last write operation on RTC registers has finished */
		RTC_WaitForLastTask();

		/* Set RTC prescaler: set RTC period to 1sec */
		RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */

		/* Wait until last write operation on RTC registers has finished */
		RTC_WaitForLastTask();

		BKP_WriteBackupRegister(BKP_DR19, 0x5a5a);
	}
    else
    {
		RTC_WaitForSynchro();
		/* Enable the RTC Second */
		RTC_ITConfig(RTC_IT_SEC, ENABLE);
		/* Wait until last write operation on RTC registers has finished */
		RTC_WaitForLastTask();
    }
    /*RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);   //RCC_RTCCLKSource_LSE--32.768KHz
    RCC_RTCCLKCmd(ENABLE);
    RTC_WaitForSynchro();
    RTC_WaitForLastTask();
    RTC_ITConfig(RTC_IT_SEC, ENABLE);
    RTC_WaitForLastTask();
    RTC_SetPrescaler(32768);//
    RTC_WaitForLastTask();*/
#else
#error Please select the RTC Clock source inside the main.c file
#endif

	/*NVIC Configuration*/
    /*NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);*/
}/*end of RTC_Configuration*/

/**
* @brief	CAN1 cnt_to_time
* @param	argument: 	t_tm *output_time: 		Conversion result output, YY-MM-DD-HH-MIN-SEC
*						U32 input_time_cnt:	Input the time counter (Unit: s)
* @note     If you want to convert system time, get the RTC counter is necessary.
			Call the function on RTC layner "RTC_GetCounter();" to get the RTC counter.
* @retval   none
*
* 
*/
void cnt_to_time(U32 input_time_cnt, t_tm *output_time)
{
	U32 cnt_temp = 0;
	t_tm time_ret = {0};
	U16 year_temp = 0;
	U16 day_temp = 0;
	U8 is_leap_or_flat_year = FLAT_YEAR, i = 0;
	U8 flat_year_month_day[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	U8 leap_year_month_day[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	cnt_temp = input_time_cnt;
	
	/*Start from 1970, form a group every 4 years, it's total 126230400.*/
	/*    1970    |    1971    |    1972    |    1973    */
	/*------------+------------+------------+------------*/
	/*    Flat    |    Flat    |    Leap    |    Flat    */
	/*===================================================*/
	/*    1974    |    1975    |    1976    |    1977    */
	/*------------+------------+------------+------------*/
	/*    Flat    |    Flat    |    Leap    |    Flat    */
	while(cnt_temp >= 126230400)
	{
		year_temp ++;
		cnt_temp = cnt_temp - 126230400;
	}
      
	time_ret.t_year = UNIX_TIME_YEAR + (4 * year_temp);			//Base of each 4 years
	/*Flat year*/
	if(cnt_temp >= 31536000)
	{
		time_ret.t_year ++;
		cnt_temp -= 31536000;
			/*Flat year*/
		if(cnt_temp >= 31536000)
		{
			time_ret.t_year ++;
			cnt_temp -= 31536000;
			/*Leap year*/
			if(cnt_temp >= 31622400)
			{
				time_ret.t_year ++;
				cnt_temp -= 31622400;
					/*Flat year*/
				if(cnt_temp >= 31536000)
				{
					time_ret.t_year ++;
					cnt_temp -= 31536000;
				}	
			}
		}
	}
	/*At last, the left second counter is within one year*/
	
	/*Judge this year is flat or leap?*/
	if((((time_ret.t_year % 4) == 0) && ((time_ret.t_year % 100) != 0)) || ((time_ret.t_year % 400) == 0)) 
	{
		is_leap_or_flat_year = LEAP_YEAR;
	}
	else
	{
		is_leap_or_flat_year = FLAT_YEAR;
	}	
	
	day_temp = cnt_temp / 86400;	
	//time_ret.t_date = cnt_temp / 86400;														//Calculate days
    time_ret.t_hour = (cnt_temp - day_temp * 86400) / 3600; 							//Calculate hours
    time_ret.t_min = (cnt_temp - day_temp * 86400 - time_ret.t_hour * 3600) / 60;	//Calculate minutes
    time_ret.t_sec = cnt_temp % 60;                                                     	//Calculate seconds

	time_ret.t_mounth = 1;
	for(i = 0; i < 12; i ++)
	{
		if(is_leap_or_flat_year == FLAT_YEAR)
		{
			if(day_temp >= flat_year_month_day[i + 1])
			{
				time_ret.t_mounth ++;
				day_temp -= flat_year_month_day[i + 1];
			}
		}	
		else if(is_leap_or_flat_year == LEAP_YEAR)
		{
			if(day_temp >= leap_year_month_day[i + 1])
			{
				time_ret.t_mounth ++;
				day_temp -= leap_year_month_day[i + 1];
			}
		}	
	}
	output_time->t_year = time_ret.t_year;
	output_time->t_mounth = time_ret.t_mounth;
	output_time->t_date = day_temp + 1;
	output_time->t_hour = time_ret.t_hour;
	output_time->t_min = time_ret.t_min;
	output_time->t_sec = time_ret.t_sec;
}/*end of cnt_to_time()*/

/**
* @brief	time_to_second
* @param	argument: 	t_tm *tt_time: 		Conversion result output, YY-MM-DD-HH-MIN-SEC
* @note     If you want to convert system time, get the RTC counter is necessary.
			Call the function on RTC layner "RTC_GetCounter();" to get the RTC counter.
* @retval   the Unix timex
*
* 
*/
U32 time_to_second(t_tm *input_time)
{
	U32 time_to_unix_clk_cnt = 0;
	U16 year_upto_date = 0, i = 0;
	U8 leap_number = 0, normal_year_number = 0, past_month_number = 0;
	t_tm cnt_time = {0};    				//Store input time
	cnt_time.t_year = input_time -> t_year;				
	cnt_time.t_mounth = input_time -> t_mounth;			
	cnt_time.t_date = input_time -> t_date;			
	cnt_time.t_hour = input_time -> t_hour;			
	cnt_time.t_min = input_time -> t_min;			
	cnt_time.t_sec = input_time -> t_sec;
	
	year_upto_date = cnt_time.t_year;
	for(i = UNIX_TIME_YEAR; i < year_upto_date; i ++)				//calculate how much leap and normal year from 1970 to last year
	{
		if((((i % 4) == 0) && ((i % 100) != 0)) || ((i % 400) == 0)) 
		{
			leap_number ++;
		}
		else
		{
			normal_year_number ++;
		}
	}
	time_to_unix_clk_cnt = leap_number * 31622400 + normal_year_number * 31536000;
	for(i = 1; i <= cnt_time.t_mounth; i ++)
	{
		past_month_number = i - 1;
		if((past_month_number == 1) || (past_month_number == 3) || (past_month_number == 5)\
									|| (past_month_number == 7) || (past_month_number == 8) || (past_month_number == 10) \
									|| (past_month_number == 12))
		{
			time_to_unix_clk_cnt += 2678400;
		}
		else if((past_month_number == 4) || (past_month_number == 6) || (past_month_number == 9) || (past_month_number == 11))
		{
			time_to_unix_clk_cnt += 2592000;
		}
		else if (past_month_number == 2)
		{
			if((((year_upto_date % 4) == 0) && ((year_upto_date % 100) != 0)) || ((year_upto_date % 400) == 0))                    //??
			{
				time_to_unix_clk_cnt += 2505600;
			}
			else
			{
				time_to_unix_clk_cnt += 2419200;
			}        						
		}
	}
	time_to_unix_clk_cnt = time_to_unix_clk_cnt + ((cnt_time.t_date - 1) * 86400);
	time_to_unix_clk_cnt = time_to_unix_clk_cnt + (cnt_time.t_hour * 3600);
	time_to_unix_clk_cnt = time_to_unix_clk_cnt + (cnt_time.t_min * 60);
	time_to_unix_clk_cnt = time_to_unix_clk_cnt + (cnt_time.t_sec);
	return time_to_unix_clk_cnt;
}/*end of time_to_second()*/

/**
* @brief	translate the gtm time to local time square time count
* @param	time_square: time square from -12 tp +12
            input_time: gtm time input
* @note     If you want to convert system time, get the RTC counter is necessary.
			Call the function on RTC layner "RTC_GetCounter();" to get the RTC counter.
* @retval   the local square time count
*
* 
*/
U32 gtm_time_trans_areas_cnt(t_gtm time_square, t_tm *input_time)
{
	U32 unix_time_clk = 0;
	S16 i;
	unix_time_clk = time_to_second(input_time);
	if(time_square > 0)
	{
		for(i = 0; i < time_square; i ++)
		{
			unix_time_clk += 3600;
		}
	}
	else if(time_square < 0)
	{
		for(i = time_square; i < 0; i ++)
		{
			unix_time_clk -= 3600;
		}
	}
	return unix_time_clk;
}/*end of gtm_time_trans_areas_cnt()*/

/**
* @brief	translate the local time square time to gtm time count
* @param	time_square: time square from -12 tp +12
            input_time: local square time input
* @note     If you want to convert system time, get the RTC counter is necessary.
			Call the function on RTC layner "RTC_GetCounter();" to get the RTC counter.
* @retval   the gtm time count
*
* 
*/
U32 local_time_trans_gtm_cnt(t_gtm time_square, t_tm *input_time)
{
	U32 unix_time_clk = 0;
	S16 i;
	unix_time_clk = time_to_second(input_time);
	if(time_square > 0)
	{
		for(i = 0; i < time_square; i ++)
		{
			unix_time_clk -= 3600;
		}
	}
	else if(time_square < 0)
	{
		for(i = time_square; i < 0; i ++)
		{
			unix_time_clk += 3600;
		}
	}
	return unix_time_clk;
}/*end of local_time_trans_gtm_cnt()*/

/**
* @brief	translate the gtm time square time local square time
* @param	time_square: time square from -12 tp +12
            input_time: gtm time input
            input_time: local square time output 
* @note     If you want to convert system time, get the RTC counter is necessary.
			Call the function on RTC layner "RTC_GetCounter();" to get the RTC counter.
* @retval   none
*
* 
*/
void gtm_time_to_trans_areas(t_gtm time_square, t_tm *input_time, t_tm *output_time)
{
	U32 	  	temp_cnt = 0;
	t_tm 	  	tt_time_back = {0};
	temp_cnt = gtm_time_trans_areas_cnt(time_square, input_time);
	cnt_to_time(temp_cnt, &tt_time_back);
	output_time->t_year = tt_time_back.t_year;
	output_time->t_mounth = tt_time_back.t_mounth;
	output_time->t_date = tt_time_back.t_date;
	output_time->t_hour = tt_time_back.t_hour;
	output_time->t_min = tt_time_back.t_min;
	output_time->t_sec = tt_time_back.t_sec;
}/*end of gtm_time_to_trans_areas()*/

/**
* @brief	get current time square time 
* @param	none
* @note     If you want to convert system time, get the RTC counter is necessary.
			Call the function on RTC layner "RTC_GetCounter();" to get the RTC counter.
* @retval   none
*
* 
*/
void get_current_time(void)
{
	U32 cnt_temp;
	t_tm tt_time = {0};
	cnt_temp = RTC_GetCounter();
	cnt_to_time(cnt_temp, &tt_time);
	gtm_time_to_trans_areas(GTM_E8, &tt_time, &sys_time);
}/*end of get_current_time()*/

/**
* @brief	translate the local square time square to gtm time
* @param	time_square: time square from -12 tp +12
            input_time: local square time input
            output_time: gtm time output
* @note     If you want to convert system time, get the RTC counter is necessary.
			Call the function on RTC layner "RTC_GetCounter();" to get the RTC counter.
* @retval   none
*
* 
*/
void local_time_to_gtm(t_gtm time_square, t_tm *input_time, t_tm *output_time)
{
	U32 	  	temp_cnt = 0;
	t_tm 	  	tt_time_back = {0};
	temp_cnt = local_time_trans_gtm_cnt(time_square, input_time);			//Get current UTX counter
	cnt_to_time(temp_cnt, &tt_time_back);
	set_gtm_rtc_counter(temp_cnt);
//	output_time->t_year = tt_time_back.t_year;
//	output_time->t_mounth = tt_time_back.t_mounth;
//	output_time->t_date = tt_time_back.t_date;
//	output_time->t_hour = tt_time_back.t_hour;
//	output_time->t_min = tt_time_back.t_min;
//	output_time->t_sec = tt_time_back.t_sec;
}/*end of local_time_to_gtm()*/

/**
* @brief	set the rtc time count register
* @param	rtc_counter: the rtc time value
* @note     If you want to convert system time, get the RTC counter is necessary.
			Call the function on RTC layner "RTC_GetCounter();" to get the RTC counter.
* @retval   none
*
* 
*/
void set_gtm_rtc_counter(U32 rtc_counter)
{
	RTC_WaitForLastTask();
	RTC_SetCounter(rtc_counter);
	RTC_WaitForLastTask();
}/*end of set_gtm_rtc_counter()*/

/**
* @brief	Initial the RTC.
* @param	none
* @note     none
* @retval   none
*/
void RTC_Initial(void)
{
	RTC_Configuration();
}/*end of RTC_Initial*/

/**
* @brief	This task updates the Time of Day clock.
* @param	none
* @note     none
* @retval   none
*/
void TOD_task(void const *argument) 
{
    // Initialization 
    task_status[UPDT_TOD_ID] = ALIVE;

#if defined(USING_ONCHIP_RTC)
    RTC_Initial();
#else
    DS1302_Initial();
#endif
    // Tasks must run in an infinite loop:
    while (TRUE) 
    {
        if(regulate_time_flag == RTC_SET_HOURS)
        {
            set_time.t_year = sys_time.t_year;
            set_time.t_mounth = sys_time.t_mounth;
            set_time.t_date = sys_time.t_date;
            local_time_to_gtm(GTM_E8, &set_time, NULL);
            regulate_time_flag = 0;
        }
        if(regulate_time_flag == RTC_SET_MONTH)
        {
        {
            set_time.t_hour = sys_time.t_hour;
            set_time.t_min = sys_time.t_min;
            set_time.t_sec = sys_time.t_sec;
            
            set_time.t_year += 2000;
            local_time_to_gtm(GTM_E8, &set_time, NULL);
            regulate_time_flag = 0;
        }        
        }
        get_current_time();
        task_status[UPDT_TOD_ID] = ALIVE;      
        osDelay(UPDT_TOD_ITV);
    }  // end while infinite loop
}  // end task_updt_TOD()  

u32 GetSystemTime(void)
{
    u32 temp_data = 0;
    int i;
    u16 year;
    u32 temp_time = 0; // convert to seconds

    
    year  = 2000 + sys_time.t_year;

    for(i = 1970; i < year; i++)
    {
        if((((i % 4) == 0) && ((i % 100) != 0)) || ((i % 400) == 0)) // leap year
            temp_time += 366;
        else
            temp_time += 365;
    }

    temp_data = sys_time.t_mounth;

    for(i = 1; i < temp_data; i++)
    {
        if(i == 4 || i == 6 || i == 9 || i == 11)
        {
            temp_time += 30;
        }
        else if( i == 2)
        {
            if(((year % 4 == 0) && (year % 100 != 0)) || ((year % 400) == 0)) // leap year
                temp_time += 29;
            else
                temp_time += 28;
        }
        else
        {
            temp_time += 31;
        }
    }

    temp_time += sys_time.t_date - 1;
    temp_time *= 24;
    
    temp_time += sys_time.t_hour;
    temp_time *= 60;

    temp_time += sys_time.t_min;
    temp_time *= 60;
    
    temp_time += sys_time.t_sec;

    //temp_time *= 1000;
    return temp_time;
}
